package com.sjgs.gis.fsrest;

import com.sjgs.common.utils.CronDateUtils;
import com.sjgs.common.utils.KryoUtils;
import com.sjgs.common.utils.UUIDUtils;
import com.sjgs.gis.domain.Chunk;
import com.sjgs.gis.domain.DataFile;
import com.sjgs.gis.domain.ScheduleJob;
import com.sjgs.gis.fs.FileSystemService;
import com.sjgs.gis.errors.InvalidChunkException;
import com.sjgs.gis.quartz.ScheduleJobService;
import com.sjgs.gis.utils.RegionUtils;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.sql.Date;
import java.time.Instant;

import static com.sjgs.common.Constants.PARAMS_SEPARATOR;
import static com.sjgs.common.Constants.SECOND;
import static com.sjgs.common.Constants.SEP;


@RestController
@RequestMapping("/upload-service")
public class FileSystemController {

    @Value("${filesystem.rest-chunk-size}")
    private Long restChunkSize;

    private final FileSystemService fsservice;

    public FileSystemController(FileSystemService fsservice) {
        this.fsservice = fsservice;
    }

    /**
     * update file test
     *
     * @param fname
     * @return
     */
    @PutMapping(value = "/updatefile/{fname}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<DataFile> updateFile(@PathVariable("fname") String fname) {
        //TODO 文件按照分片更新方法
        //TODO 1-比对分片hash相似度达到50%视为更新，否则视为新文件
        return fsservice.getDataFileByName(fname);
    }

    /**
     * make file
     *
     * @param fsize
     * @param dir
     * @param storeType
     * @param fkey
     * @return
     */
    @PostMapping(value = "/mkfile/{fkey}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<String> makeFile(@RequestHeader("Up-Length") Long fsize,
                                 @RequestHeader(value = "Store-Dir", required = false) String dir,
                                 @RequestHeader(value = "Store-Type", required = false) String storeType,
                                 @PathVariable("fkey") String fkey) {
        return fsservice.checkFile(fkey, fsize, dir, storeType);
    }

    /**
     * checkFileCompleted
     * 所有片传输完成以后调用，完成同步文件到集群或者后端文件系统
     *
     * @param count
     * @param fkey
     * @return
     */
    @PostMapping(value = "/chkfile/{count}/fkey/{fkey}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<String> checkFileCompleted(@PathVariable("count") Long count,
                                           @PathVariable("fkey") String fkey
    ) {
        return fsservice.addJobForCheckFileCompleted(fkey,count);
    }

    /**
     * check chunk is complete
     *
     * @param fkey
     * @param index
     * @param hash
     * @return
     */
    @GetMapping(value = "/chkchunk/{fkey}/index/{index}/hash/{hash}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono<Boolean> ckeckChunk(@PathVariable("fkey") String fkey,
                                    @PathVariable("index") Long index,
                                    @PathVariable("hash") String hash) {
        return fsservice.checkChunk(fkey, index, hash).flatMap(chunk -> {
            return Mono.just(false);
        }).switchIfEmpty(Mono.just(true));
    }

    /**
     * put chunk to disk
     *
     * @param parts
     * @param context
     * @param range
     * @param index
     * @param hash
     * @return
     */
    @PostMapping(value = "/putchunk/{fkey}/index/{index}/hash/{hash}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public Mono<Chunk> putChunk(@RequestBody Flux<Part> parts,
                                @RequestHeader("Up-Context") String context,
                                @RequestHeader("Range") String range,
                                @PathVariable("fkey") String fkey,
                                @PathVariable("index") Long index,
                                @PathVariable("hash") String hash) {
        if (context.length() < 10)
            throw new InvalidChunkException();
        DataFile df = KryoUtils.deserializeObject(Base64.decodeBase64(context), DataFile.class);
        Long fsize = df.getFsize();
        Long chunkCount = RegionUtils.countOf(fsize, restChunkSize);
        Long chunkStart = Long.valueOf(range.split("-")[0]);
        Long chunkEnd = Long.valueOf(range.split("-")[1]);
        Chunk chunk = new Chunk(fkey, chunkCount, index, hash, chunkStart, chunkEnd);
        return parts.log().
                collectList()
                .flatMap(list -> {
                    if (list.size() > 0) {
                        return fsservice.putChunk(df, chunk, (FilePart) list.get(0))
                                .onErrorResume(e -> {
                                    throw new InvalidChunkException();
                                });
                    } else {
                        return Mono.empty();
                    }
                }).onErrorResume(e -> {
                    throw new InvalidChunkException();
                });
    }


}
